/* GMFSController.m -- 
   Copyright (C) 2007 Casey Marshall <casey.s.marshall@gmail.com>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  */


#import <Security/Security.h>
#import "GMFSController.h"
#import "GMFSMountInfo.h"

#import <stdio.h>
#import <unistd.h>

@implementation GMFSController

#define kGMFSService "GMailFS"

#define kGMFSAccounts         @"accounts"
#define kGMFSAccountName      @"name"
#define kGMFSAccountUser      @"user"
#define kGMFSAccountUseProxy  @"useProxy"
#define kGMFSAccountProxy     @"proxy"
#define kGMFSAccountAutoMount @"autoMount"

- (void) awakeFromNib
{
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  [defaults registerDefaults:
    [NSDictionary dictionaryWithObjectsAndKeys:
      [NSArray array], kGMFSAccounts,
      nil]];
  
  [[NSNotificationCenter defaultCenter]
    addObserver: self
       selector: @selector(tableSelectionChanged:)
           name: NSTableViewSelectionDidChangeNotification
         object: nil];
  [[NSNotificationCenter defaultCenter]
    addObserver: self
       selector: @selector(fstaskExited:)
           name: NSTaskDidTerminateNotification
         object: nil];
  
  
  NSArray *accounts = [defaults arrayForKey: kGMFSAccounts];
  NSEnumerator *e = [accounts objectEnumerator];
  id obj;
  int i = 0;
  while ((obj = [e nextObject]) != nil)
  {
    NSDictionary *account = obj;
    NSString *keyEquiv = @"";
    if (i < 10)
      keyEquiv = [NSString stringWithFormat: @"%d", i++];
    NSMenuItem *item = [[NSMenuItem alloc]
      initWithTitle: [account objectForKey: kGMFSAccountName]
             action: @selector(mountFromMainMenu:)
      keyEquivalent: keyEquiv];
    [item autorelease];
    [item setTarget: self];
    if (i < 10)
      [item setKeyEquivalentModifierMask: NSCommandKeyMask];
    i++;
    [disksMenu addItem: item];
    
    item = [[NSMenuItem alloc]
      initWithTitle: [account objectForKey: kGMFSAccountName]
             action: @selector(mountFromDockMenu:)
      keyEquivalent: @""];
    [item autorelease];
    [item setTarget: self];
    [dockMenu addItem: item];
  }
  
  filesystems = [[NSMutableDictionary alloc] init];
}

- (IBAction) showAccounts: (id) sender
{
  [mainWindow center];
  [mainWindow makeKeyAndOrderFront: self];
}

#pragma mark Table selection notification

- (void) tableSelectionChanged: (NSNotification *) aNotification
{
  int sel = [accountsTable selectedRow];
  if (sel < 0)
  {
    [delButton setEnabled: NO];
    [usernameField setStringValue: @""];
    [usernameField setEnabled: NO];
    [passwordField setStringValue: @""];
    [passwordField setEnabled: NO];
    [proxyEnabled setState: NSOffState];
    [proxyEnabled setEnabled: NO];
    [proxyField setStringValue: @""];
    [proxyField setEnabled: NO];
    [autoMount setState: NSOffState];
    [autoMount setEnabled: NO];
  }
  else
  {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    
    NSArray *accounts = [defaults arrayForKey: kGMFSAccounts];
    if (sel >= [accounts count])
      return;
    
    NSDictionary *account = [accounts objectAtIndex: sel];
    if (account == nil)
      return;
    
    [delButton setEnabled: YES];
    [usernameField setStringValue:
      (NSString *) [account objectForKey: kGMFSAccountUser]];
    [usernameField setEnabled: YES];
    [passwordField setStringValue: @""];
    [passwordField setEnabled: YES];
    [proxyEnabled setState:
      [(NSNumber *) [account objectForKey: kGMFSAccountUseProxy] boolValue]
      ? NSOnState
      : NSOffState];
    [proxyEnabled setEnabled: YES];
    [proxyField setStringValue:
      [account objectForKey: kGMFSAccountProxy]];
    [proxyField setEnabled: [proxyEnabled state] == NSOnState];
    [autoMount setState:
      [(NSNumber *) [account objectForKey: kGMFSAccountAutoMount] boolValue]
      ? NSOnState
      : NSOffState];
    [autoMount setEnabled: YES];
  }
}

- (void) fstaskExited: (NSNotification *) aNotification
{
  NSLog (@"fstask exited");
  NSEnumerator *e = [[filesystems allValues] objectEnumerator];
  id obj;
  while ((obj = [e nextObject]) != nil)
  {
    GMFSMountInfo *mi = obj;
    if (![[mi fstask] isRunning])
    {
      NSString *accountName = [mi fsname];
      NSLog (@"task for filesystem %@ finished", accountName);
      NSMenuItem *item = [disksMenu itemWithTitle: accountName];
      if (item != nil)
        [item setState: NSOffState];
      item = [dockMenu itemWithTitle: accountName];
      if (item != nil)
        [item setState: NSOffState];
      [filesystems removeObjectForKey: accountName];
      unlink ([[mi configFile] UTF8String]);
      rmdir ([[mi mountPoint] UTF8String]);
    }
  }
}

#pragma mark Methods modifying user defaults

- (BOOL) nameIsOkay: (NSString *) name
           accounts: (NSArray *) array
{
  if ([name isEqualToString: [[disksMenu itemAtIndex: 0] title]]
      || [name isEqualToString: [[dockMenu itemAtIndex: 0] title]])
    return NO;
    
  id obj;
  NSEnumerator *e = [array objectEnumerator];
  while ((obj = [e nextObject]) != nil)
  {
    NSString *n = [((NSDictionary *) obj) objectForKey: kGMFSAccountName];
    if ([n isEqualToString: name])
      return NO;
  }
  return YES;
}

- (IBAction) addAccount: (id) sender
{
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  NSMutableArray *accounts = [NSMutableArray arrayWithArray:
    [defaults arrayForKey: kGMFSAccounts]];
  NSString *newName = @"New Account";
  
  int i = 1;
  while (![self nameIsOkay: newName accounts: accounts])
  {
    newName = [NSString stringWithFormat: @"New Account %d", i++];
  }
  
  NSDictionary *account = [NSDictionary dictionaryWithObjectsAndKeys:
    newName, kGMFSAccountName,
    @"", kGMFSAccountUser,
    [NSNumber numberWithBool: NO], kGMFSAccountUseProxy,
    @"", kGMFSAccountProxy,
    [NSNumber numberWithBool: NO], kGMFSAccountAutoMount,
    nil];
  
  [accounts addObject: account];
  [defaults setObject: accounts
               forKey: kGMFSAccounts];
  [defaults synchronize];
  
  int n = [accounts count] - 1;
  NSString *keyEquivalent = @"";
  if (n < 10)
    keyEquivalent = [NSString stringWithFormat: @"%d", n];
  NSMenuItem *item = [[NSMenuItem alloc]
    initWithTitle: newName
           action: @selector(mountFromMainMenu:)
    keyEquivalent: keyEquivalent];
  [item autorelease];
  [item setTarget: self];
  if (n < 10)
    [item setKeyEquivalentModifierMask: NSCommandKeyMask];
  [disksMenu addItem: item];
  
  item = [[NSMenuItem alloc]
    initWithTitle: newName
           action: @selector(mountFromDockMenu:)
    keyEquivalent: @""];
  [item autorelease];
  [item setTarget: self];
  [dockMenu addItem: item];
  
  [accountsTable selectRowIndexes:
    [NSIndexSet indexSetWithIndex: n]
             byExtendingSelection: NO];
  [accountsTable reloadData];
}

- (IBAction) delAccount: (id) sender
{
  int idx = [accountsTable selectedRow];
  if (idx < 0)
    return;

  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  NSMutableArray *accounts = [NSMutableArray arrayWithArray:
    [defaults arrayForKey: kGMFSAccounts]];
  NSDictionary *account = [accounts objectAtIndex: idx];
  
  int mindex = [disksMenu indexOfItemWithTitle:
    [account objectForKey: kGMFSAccountName]];
  if (mindex > 0)
    [disksMenu removeItemAtIndex: mindex];
  mindex = [dockMenu indexOfItemWithTitle:
    [account objectForKey: kGMFSAccountName]];
  if (mindex > 0)
    [dockMenu removeItemAtIndex: mindex];
  
  [accounts removeObjectAtIndex: idx];
  [defaults setObject: accounts
               forKey: kGMFSAccounts];
  [defaults synchronize];
  
  NSEnumerator *e = [accounts objectEnumerator];
  id obj;
  int i = 0;
  while ((obj = [e nextObject]) != nil)
  {
    NSDictionary *account = obj;
    NSMenuItem *item = [disksMenu itemWithTitle:
      [account objectForKey: kGMFSAccountName]];
    if (i < 10)
    {
      [item setKeyEquivalent: [NSString stringWithFormat: @"%d", i]];
      [item setKeyEquivalentModifierMask: NSCommandKeyMask];
      i++;
    }
  }
  
  [accountsTable selectRowIndexes: [NSIndexSet indexSet]
             byExtendingSelection: NO];
  [accountsTable reloadData];
}

- (IBAction) setUsername: (id) sender
{
  int idx = [accountsTable selectedRow];
  if (idx < 0)
    return;

  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  NSMutableArray *accounts = [NSMutableArray arrayWithArray:
    [defaults arrayForKey: kGMFSAccounts]];
  NSMutableDictionary *account = [NSMutableDictionary
    dictionaryWithDictionary: [accounts objectAtIndex: idx]];
  [account setObject: [usernameField stringValue]
              forKey: kGMFSAccountUser];
  [accounts replaceObjectAtIndex: idx
                      withObject: account];
  [defaults setObject: accounts
               forKey: kGMFSAccounts];
  [defaults synchronize];
  
  // FIXME unmount and remount?
}

- (IBAction) toggleUseProxy: (id) sender
{
  int idx = [accountsTable selectedRow];
  if (idx < 0)
    return;
  
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  NSMutableArray *accounts = [NSMutableArray arrayWithArray:
    [defaults arrayForKey: kGMFSAccounts]];
  NSMutableDictionary *account = [NSMutableDictionary
    dictionaryWithDictionary: [accounts objectAtIndex: idx]];
  [account setObject: [NSNumber numberWithBool: [proxyEnabled state] == NSOnState]
              forKey: kGMFSAccountUseProxy];
  [accounts replaceObjectAtIndex: idx
                      withObject: account];
  [defaults setObject: accounts
               forKey: kGMFSAccounts];
  [defaults synchronize];
  
  [proxyField setEnabled: [proxyEnabled state] == NSOnState];
  // FIXME unmount and remount?
}

- (IBAction) setProxy: (id) sender
{
  int idx = [accountsTable selectedRow];
  if (idx < 0)
    return;
  
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  NSMutableArray *accounts = [NSMutableArray arrayWithArray:
    [defaults arrayForKey: kGMFSAccounts]];
  NSMutableDictionary *account = [NSMutableDictionary
    dictionaryWithDictionary: [accounts objectAtIndex: idx]];
  [account setObject: [proxyField stringValue]
              forKey: kGMFSAccountProxy];
  [accounts replaceObjectAtIndex: idx
                      withObject: account];
  [defaults setObject: accounts
               forKey: kGMFSAccounts];
  [defaults synchronize];
  
  // FIXME unmount and remount?
}

- (IBAction) toggleAutoMount: (id) sender
{
  int idx = [accountsTable selectedRow];
  if (idx < 0)
    return;
  
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  NSMutableArray *accounts = [NSMutableArray arrayWithArray:
    [defaults arrayForKey: kGMFSAccounts]];
  NSMutableDictionary *account = [NSMutableDictionary
    dictionaryWithDictionary: [accounts objectAtIndex: idx]];
  [account setObject: [NSNumber numberWithBool: [autoMount state] == NSOnState]
              forKey: kGMFSAccountAutoMount];
  [accounts replaceObjectAtIndex: idx
                      withObject: account];
  [defaults setObject: accounts
               forKey: kGMFSAccounts];
  [defaults synchronize];
  // FIXME unmount and remount?
}

#pragma mark Methods modifying the user's keychain

- (IBAction) setPassword: (id) sender
{
  int idx = [accountsTable selectedRow];
  if (idx < 0)
    return;
  
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  NSArray *accounts = [defaults arrayForKey: kGMFSAccounts];
  NSDictionary *account = [accounts objectAtIndex: idx];
  
  NSString *accountName = [account objectForKey: kGMFSAccountName];
  const char *accountNameStr = [accountName UTF8String];
  NSString *password = [passwordField stringValue];
  const char *passwordStr = [password UTF8String];
  OSStatus result = SecKeychainAddGenericPassword (NULL,
                                                   strlen (kGMFSService),
                                                   kGMFSService,
                                                   strlen (accountNameStr),
                                                   accountNameStr,
                                                   strlen (passwordStr),
                                                   (const void *) passwordStr,
                                                   NULL);
}

#pragma mark Mount and unmount

- (void) mount: (NSDictionary *) mountInfo
{
  NSString *accountName = [mountInfo objectForKey: kGMFSAccountName];
  const char *accountNameStr = [accountName UTF8String];
  void *password;
  UInt32 passwordLength;
  OSStatus result = SecKeychainFindGenericPassword (NULL,
                                                    strlen (kGMFSService),
                                                    kGMFSService,
                                                    strlen (accountNameStr),
                                                    accountNameStr,
                                                    &passwordLength,
                                                    &password,
                                                    NULL);

  char *passwordStr = NULL;
  if (result == noErr)
  {
    passwordStr = (char *) malloc (passwordLength + 1);
    memset (passwordStr, 0, passwordLength + 1);
    memcpy (passwordStr, password, passwordLength);
    [NSData dataWithBytesNoCopy: passwordStr
                         length: passwordLength + 1];
    SecKeychainItemFreeContent (NULL, password);
  }
  else
  {
    [passwordDialogSavePassword setState: NSOffState];
    [passwordDialogField setStringValue: @""];
    [passwordDialogLabel setStringValue:
      [NSString stringWithFormat: @"Enter password for Gmail account \"%@:\"",
        [mountInfo objectForKey: kGMFSAccountUser]]];
    int ret = [NSApp runModalForWindow: passwordDialog];
    if (ret != NSRunStoppedResponse)
    {
      // Cancelled; just exit.
      return;
    }
    passwordStr = [[passwordDialogField stringValue] UTF8String];
    
    if ([passwordDialogSavePassword state] == NSOnState)
    {
      result = SecKeychainAddGenericPassword (NULL,
                                              strlen (kGMFSService),
                                              kGMFSService,
                                              strlen (accountNameStr),
                                              accountNameStr,
                                              strlen (passwordStr),
                                              (const void *) passwordStr,
                                              NULL);
    }
  }

  char configFile[20];
  memset (configFile, 0, 20);
  strncpy (configFile, "/tmp/gmailfs.XXXXXX", 20);
  int fd = mkstemp (configFile);
  if (fd < 0)
    return;
  FILE *file = fdopen (fd, "w");
  
  fprintf (file, "[connection]\n");
  if ([[mountInfo objectForKey: kGMFSAccountUseProxy] boolValue])
  {
    fprintf (file, "proxy = %s\n",
             [[mountInfo objectForKey: kGMFSAccountProxy] UTF8String]);
  }
  fprintf (file, "\n[account]\n");
  fprintf (file, "username = %s\n",
           [[mountInfo objectForKey: kGMFSAccountUser] UTF8String]);
  fprintf (file, "password = %s\n\n", passwordStr);
  fprintf (file, "[filesystem]\n");
  fprintf (file, "fsname = %s\n\n", [accountName UTF8String]);
  fprintf (file, "[logs]\n");
  fprintf (file, "level = %s\n", GMAILFS_LOG_LEVEL);
  fflush (file);
  fclose (file);
  NSString *configFilePath = [NSString stringWithUTF8String: configFile];

  NSFileManager *fm = [NSFileManager defaultManager];
  NSString *mountPoint = [NSString stringWithFormat: @"/Volumes/%@",
    accountName];
  int tries = 0;
  while ([fm fileExistsAtPath: mountPoint])
    mountPoint = [NSString stringWithFormat: @"/Volumes/%@ %d", accountName,
      ++tries];
  [fm createDirectoryAtPath: mountPoint
                 attributes: nil];
  NSString *mount_gmailfs
    = [[NSBundle mainBundle] pathForResource: @"mount.gmailfs"
                                      ofType: @"py"
                                 inDirectory: @"Python"];
  NSString *gmailfs_py
    = [[NSBundle mainBundle] pathForResource: @"gmailfs"
                                      ofType: @"py"
                                 inDirectory: @"Python"];
  NSString *pypath = [[[NSBundle mainBundle] resourcePath]
    stringByAppendingPathComponent: @"Python"];
  
  NSTask *task = [[NSTask alloc] init];
  [task setLaunchPath: @"/usr/bin/python"];
  NSArray *argv = [NSArray arrayWithObjects:
    mount_gmailfs,
    [NSString stringWithFormat:
      @"-ovolname=%@,fsname=%@,username=%@,password=%s",
      accountName, accountName, [mountInfo objectForKey: kGMFSAccountUser],
      passwordStr],
    gmailfs_py,
    mountPoint,
    nil];
  NSLog (@"/usr/bin/python %@", argv);
  [task setArguments: argv];
  [task setEnvironment: [NSDictionary dictionaryWithObjectsAndKeys:
    @"0.1", @"FUSE_PYTHON_COMPAT",
    pypath, @"PYTHONPATH",
    configFilePath, @"GMAILFS_CONFIG_FILE",
    nil]];
  
  NSMenuItem *item = [disksMenu itemWithTitle: accountName];
  if (item != nil)
    [item setState: NSOnState];
  item = [dockMenu itemWithTitle: accountName];
  if (item != nil)
    [item setState: NSOnState];
  
  [task launch];

  [filesystems setObject:
    [GMFSMountInfo mountInfoWithFSName: accountName
                            mountPoint: mountPoint
                            configFile: configFilePath
                                  task: task]
                  forKey: accountName];
}

- (void) unmount: (NSString *) accountName
{
  NSEnumerator *e = [[filesystems allValues] objectEnumerator];
  id obj;
  while ((obj = [e nextObject]) != nil)
  {
    GMFSMountInfo *mi = obj;
    if ([[mi fsname] isEqualToString: accountName])
    {
      NSTask *task = [mi fstask];
      if ([task isRunning])
      {
        [task terminate];
      }
    }
  }
}

- (IBAction) mountFromMainMenu: (id) sender
{
  NSString *accountName = [sender title];
  NSEnumerator *e = [[filesystems allValues] objectEnumerator];
  id obj;
  while ((obj = [e nextObject]) != nil)
  {
    GMFSMountInfo *mi = obj;
    if ([[mi fsname] isEqualToString: accountName])
    {
      if ([[mi fstask] isRunning])
      {
        [self unmount: accountName];
        return;
      }
    }
  }
  
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  NSArray *accounts = [defaults arrayForKey: kGMFSAccounts];
  e = [accounts objectEnumerator];
  while ((obj = [e nextObject]) != nil)
  {
    NSDictionary *account = obj;
    if ([accountName isEqual: [account objectForKey: kGMFSAccountName]])
    {
      [self mount: account];
      break;
    }
  }
}

// XXX Yeah, OK, sure, maybe we don't need to distinguish these two.
- (IBAction) mountFromDockMenu: (id) sender
{
  NSString *accountName = [sender title];
  NSEnumerator *e = [[filesystems allValues] objectEnumerator];
  id obj;
  while ((obj = [e nextObject]) != nil)
  {
    GMFSMountInfo *mi = obj;
    if ([[mi fsname] isEqualToString: accountName])
    {
      if ([[mi fstask] isRunning])
      {
        [self unmount: accountName];
        return;
      }
    }
  }
  
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  NSArray *accounts = [defaults arrayForKey: kGMFSAccounts];
  e = [accounts objectEnumerator];
  while ((obj = [e nextObject]) != nil)
  {
    NSDictionary *account = obj;
    if ([accountName isEqual: [account objectForKey: kGMFSAccountName]])
    {
      [self mount: account];
      break;
    }
  }
}

#pragma mark Password dialog

- (IBAction) passwordDialogCancel: (id) sender
{
  [passwordDialogField setStringValue: @""];
  [NSApp abortModal];
  [passwordDialog orderOut: self];
}

- (IBAction) passwordDialogConnect: (id) sender
{
  [NSApp stopModal];
  [passwordDialog orderOut: self];
}

#pragma mark NSTableDataSource

- (int) numberOfRowsInTableView: (NSTableView *) view
{
  if (view == accountsTable)
  {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSArray *accounts = [defaults arrayForKey: kGMFSAccounts];
    if (accounts != nil)
      return [accounts count];
  }
  return 0;
}

- (id) tableView: (NSTableView *) aTableView
 objectValueForTableColumn: (NSTableColumn *) aTableColumn
             row: (int) rowIndex
{
  if (aTableView == accountsTable)
  {
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSArray *accounts = [defaults arrayForKey: kGMFSAccounts];
    NSDictionary *account = [accounts objectAtIndex: rowIndex];
    if (account != nil)
      return [account objectForKey: kGMFSAccountName];
  }
  return @"???";
}

- (void) tableView: (NSTableView *) aTableView
    setObjectValue: (id) anObject
    forTableColumn: (NSTableColumn *) aTableColumn
               row: (int) rowIndex
{
  if (aTableView != accountsTable)
    return;
  
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
  NSMutableArray *accounts = [NSMutableArray arrayWithArray:
    [defaults arrayForKey: kGMFSAccounts]];
  NSString *newName = [anObject description];
  NSMutableDictionary *account = [NSMutableDictionary dictionaryWithDictionary:
    [accounts objectAtIndex: rowIndex]];
  if ([newName isEqual: [account objectForKey: kGMFSAccountName]])
    return;
  if (![self nameIsOkay: newName accounts: accounts])
  {
    NSBeep();
    [accountsTable reloadData];
    return;
  }
  
  SecKeychainItemRef itemRef;
  NSString *accountName = [account objectForKey: kGMFSAccountName];
  const char *accountNameStr = [accountName UTF8String];
  void *password;
  UInt32 passwordLength;
  OSStatus result = SecKeychainFindGenericPassword (NULL,
                                                    strlen (kGMFSService),
                                                    kGMFSService,
                                                    strlen (accountNameStr),
                                                    accountNameStr,
                                                    &passwordLength,
                                                    &password,
                                                    &itemRef);
  if (result == noErr)
  {
    const char *newNameStr = [newName UTF8String];
    OSStatus result2 = SecKeychainAddGenericPassword (NULL,
                                                      strlen (kGMFSService),
                                                      kGMFSService,
                                                      strlen (newNameStr),
                                                      newNameStr,
                                                      passwordLength,
                                                      password,
                                                      NULL);
    if (result2 == noErr)
    {
      SecKeychainItemDelete (itemRef);
    }
    CFRelease (itemRef);
    SecKeychainItemFreeContent (NULL, password);
    if (result2 != noErr)
    {
      return;
    }
  }
  else if (result != errSecItemNotFound)
  {
    return;
  }
  
  // No password set yet; just modify the name.
  NSMenuItem *item = [disksMenu itemWithTitle: accountName];
  if (item != nil)
    [item setTitle: newName];
  item = [dockMenu itemWithTitle: accountName];
  if (item != nil)
    [item setTitle: newName];
  
  [account setObject: newName
              forKey: kGMFSAccountName];
  [accounts replaceObjectAtIndex: rowIndex
                      withObject: account];
  [defaults setObject: accounts
               forKey: kGMFSAccounts];
  [defaults synchronize];
  [accountsTable reloadData];
}

@end
